Step 1: Set Parameters and Download Data¶
In [2]:
import numpy as np
import pandas as pd
import datetime as dt
import yfinance as yf
# Set the number of years for historical data
years = 15
# Define the end and start dates
endDate = dt.datetime.now()
startDate = endDate - dt.timedelta(days=365 * years)
# Create a list of tickers
tickers = ['SPY', 'AAPL', 'GLD', 'NVDA', 'TSLA']
# Download the daily adjusted close prices for the tickers
adj_close_df = pd.DataFrame()
for ticker in tickers:
data = yf.download(ticker, start=startDate, end=endDate)
adj_close_df[ticker] = data['Adj Close']
# Display the downloaded data
print(adj_close_df)
[*********************100%%**********************] 1 of 1 completed [*********************100%%**********************] 1 of 1 completed [*********************100%%**********************] 1 of 1 completed [*********************100%%**********************] 1 of 1 completed [*********************100%%**********************] 1 of 1 completed
SPY AAPL GLD NVDA TSLA Date 2009-07-01 69.827072 4.312715 92.389999 0.260034 NaN 2009-07-02 67.921249 4.227867 91.250000 0.249944 NaN 2009-07-06 67.913666 4.185293 90.760002 0.241919 NaN 2009-07-07 66.597740 4.088368 90.709999 0.231371 NaN 2009-07-08 66.552353 4.143321 89.269997 0.231829 NaN ... ... ... ... ... ... 2024-06-21 544.510010 207.490005 214.779999 126.570000 183.009995 2024-06-24 542.739990 208.139999 215.630005 118.110001 182.580002 2024-06-25 544.830017 209.070007 214.559998 126.089996 187.350006 2024-06-26 545.510010 213.250000 212.580002 126.400002 196.369995 2024-06-27 545.909973 213.259903 215.300003 123.867996 195.970001 [3773 rows x 5 columns]
This code sets the time period for historical data, defines a list of tickers, and downloads their daily adjusted close prices from Yahoo Finance.¶
Step 2: Calculate Daily Log Returns¶
In [3]:
# Calculate the daily log returns and drop any NAs
log_returns = np.log(adj_close_df / adj_close_df.shift(1))
log_returns = log_returns.dropna()
# Display the log returns
print(log_returns)
SPY AAPL GLD NVDA TSLA Date 2010-06-30 -0.009546 -0.018279 0.003375 -0.026101 -0.002515 2010-07-01 -0.004466 -0.012200 -0.038879 0.016513 -0.081723 2010-07-02 -0.005465 -0.006217 0.012313 -0.012603 -0.134312 2010-07-06 0.006534 0.006820 -0.016851 -0.010790 -0.175470 2010-07-07 0.031010 0.039587 0.010417 0.047192 -0.019431 ... ... ... ... ... ... 2024-06-21 -0.001342 -0.010499 -0.015615 -0.032721 0.007899 2024-06-24 -0.003256 0.003128 0.003950 -0.069179 -0.002352 2024-06-25 0.003843 0.004458 -0.004975 0.065380 0.025790 2024-06-26 0.001247 0.019796 -0.009271 0.002456 0.047022 2024-06-27 0.000733 0.000046 0.012714 -0.020235 -0.002039 [3522 rows x 5 columns]
Step 3: Define Functions for Expected Return and Standard Deviation¶
In [5]:
# Function to calculate portfolio expected return
def expected_return(weights, log_returns):
return np.sum(log_returns.mean() * weights)
# Function to calculate portfolio standard deviation
def standard_deviation(weights, cov_matrix):
variance = weights.T @ cov_matrix @ weights
return np.sqrt(variance)
In [6]:
# Create a covariance matrix for all the securities
cov_matrix = log_returns.cov()
# Display the covariance matrix
print(cov_matrix)
SPY AAPL GLD NVDA TSLA SPY 0.000116 0.000130 0.000005 0.000189 0.000166 AAPL 0.000130 0.000310 0.000008 0.000245 0.000227 GLD 0.000005 0.000008 0.000095 0.000003 0.000014 NVDA 0.000189 0.000245 0.000003 0.000797 0.000367 TSLA 0.000166 0.000227 0.000014 0.000367 0.001270
In [7]:
# Create an equally weighted portfolio
portfolio_value = 1000000
weights = np.array([1 / len(tickers)] * len(tickers))
# Calculate the portfolio's expected return and standard deviation
portfolio_expected_return = expected_return(weights, log_returns)
portfolio_std_dev = standard_deviation(weights, cov_matrix)
In [9]:
# Function to generate a random z-score from a normal distribution
def random_z_score():
return np.random.normal(0, 1)
In [10]:
# Number of days to projectT
days = 20
# Function to calculate scenario gain/loss
def scenario_gain_loss(portfolio_value, portfolio_std_dev, z_score, days):
return portfolio_value * portfolio_expected_return * days + portfolio_value * portfolio_std_dev * z_score * np.sqrt(days)
In [11]:
# Number of simulatIons
simulations = 10000
scenarioReturn = []
# Run the simulatIons
for i in range(simulations):
z_score = random_z_score()
scenarioReturn.append(scenario_gain_loss(portfolio_value, portfolio_std_dev, z_score, days))
In [12]:
# Specify a confidence interval
confidence_interval = 0.99
# Calculate Value at Risk (VaR)
VaR = -np.percentile(scenarioReturn, 100 * (1 - confidence_interval))
# Display the VaR
print(f'Value at Risk (VaR) at {confidence_interval:.0%} confidence level: ${VaR:.2f}')
Value at Risk (VaR) at 99% confidence level: $126056.07
Value at Risk (VaR) at 99% confidence level: $126056.07¶
In [13]:
import matplotlib.pyplot as plt
# Plot the results of all 10,000 scenarios
plt.hist(scenarioReturn, bins=50, density=True)
plt.xlabel('Scenario Gain/Loss ($)')
plt.ylabel('Frequency')
plt.title(f'Distribution of Portfolio Gain/Loss Over {days} Days')
plt.axvline(-VaR, color='r', linestyle='dashed', linewidth=2, label=f'VaR at {confidence_interval:.0%} confidence level')
plt.legend()
plt.show()